<!DOCTYPE html>
<script src="/js-test-resources/gc.js"></script>
<script src="/js-test-resources/js-test.js"></script>
<script src="resources/test-helpers.js"></script>
<script>
window.jsTestIsAsync = true;
description('Test that ServiceWorker and ServiceWorkerRegistration are not garbage collected prematurely');
var registrationObservation = null;
var swObservation = null;
var scope = base_path() + 'resources/gc';

if (!window.internals) {
    testFailed('This test requires internals.observeGC');
    finishJSTest();
} else {
    setup();
}

function setup() {
    var worker = 'resources/empty-worker.js';
    unregisterAndRegister(worker, scope).then(onRegister);
}

function unregisterAndRegister(url, scope) {
    return navigator.serviceWorker.getRegistration(scope)
        .then(function(registration) {
            if (registration)
              return registration.unregister();
          })
        .then(function() {
            return navigator.serviceWorker.register(url, { scope: scope });
          })
        .catch(function(error) {
            testFailed('Could not register worker: ' + error);
            finishJSTest();
          });
}

function onRegister(registration) {
    registrationObservation = internals.observeGC(registration);
    registration.addEventListener('updatefound', (function() {
        onUpdate(registration.installing);
    }));
}

function onUpdate(sw) {
    swObservation = internals.observeGC(sw);
    sw.addEventListener('statechange', onStateChange);
}

function onStateChange(event) {
    // Use setTimeout to ensure a fresh stack with no references to the worker.
    switch (event.target.state) {
    case 'activated':
        setTimeout(unregister, 0);
        break;
    case 'redundant':
        setTimeout(finish, 0);
        break;
    }
}

function unregister() {
    // The worker has an event handler that can still receive the state change
    // to 'redundant', so it shouldn't be collected yet.
    asyncGC().then(function() {
        shouldBeFalse('registrationObservation.wasCollected');
        shouldBeFalse('swObservation.wasCollected');
        return navigator.serviceWorker.getRegistration(scope);
    }).then(function(registration) {
        registration.unregister();
    }).catch(function(error) {
        testFailed('Could not unregister worker: ' + error);
        finishJSTest();
    });
}

function finish()
{
    // The worker is 'redundant' but the registration holds a reference to it,
    // so it shouldn't be collected yet.
    // FIXME: When crbug.com/398355 is fixed, register a new script URL and
    // once the new worker is activated, check that the old worker is gc'd.
    asyncGC().then(function() {
        shouldBeFalse('registrationObservation.wasCollected');
        shouldBeTrue('swObservation.wasCollected');
        finishJSTest();
    });
}
</script>
